package com.UnionPayCenter.service.serviceImpl;


import com.UnionPayCenter.account.entity.Account;
import com.UnionPayCenter.account.entity.FundsBill;
import com.UnionPayCenter.account.enums.AccountTypeEnum;
import com.UnionPayCenter.account.service.AccountService;
import com.UnionPayCenter.account.service.ChargePayService;
import com.UnionPayCenter.constant.AccountConstants;
import com.UnionPayCenter.constant.ConstantInterface;
import com.UnionPayCenter.constant.CouponConstants;
import com.UnionPayCenter.distribution.constants.DistributionConstants;
import com.UnionPayCenter.distribution.dto.DistributionBaseDTO;
import com.UnionPayCenter.distribution.dto.DistributionShareDTO;
import com.UnionPayCenter.distribution.entity.DistributionTemplateDO;
import com.UnionPayCenter.distribution.manager.DistributionTemplateManager;
import com.UnionPayCenter.distribution.service.DistributionOperatorService;
import com.UnionPayCenter.domain.UserQuery;
import com.UnionPayCenter.entity.ChannelEntity;
import com.UnionPayCenter.entity.ConfEntity;
import com.UnionPayCenter.entity.IntegralRuleEntity;
import com.UnionPayCenter.entity.ManagerEntity;
import com.UnionPayCenter.entity.ShopEntity;
import com.UnionPayCenter.entity.ShopInfoEntity;
import com.UnionPayCenter.entity.UserEntity;
import com.UnionPayCenter.entity.UserIntegralDetailEntity;
import com.UnionPayCenter.entity.UserLevelEntity;
import com.UnionPayCenter.entity.WhiteListEntity;
import com.UnionPayCenter.entity.WhitelistSetEntity;
import com.UnionPayCenter.entity.vo.FundsBillRecordVo;
import com.UnionPayCenter.entity.vo.UserVo;
import com.UnionPayCenter.enums.ErrorCodeEnum;
import com.UnionPayCenter.enums.SexEnum;
import com.UnionPayCenter.exception.ApiException;
import com.UnionPayCenter.mapper.ChannelMapper;
import com.UnionPayCenter.mapper.ConfMapper;
import com.UnionPayCenter.mapper.IntegralRuleMapper;
import com.UnionPayCenter.mapper.ManagerMapper;
import com.UnionPayCenter.mapper.ShopInfoMapper;
import com.UnionPayCenter.mapper.ShopMapper;
import com.UnionPayCenter.mapper.UserIntegralDetailMapper;
import com.UnionPayCenter.mapper.UserLevelMapper;
import com.UnionPayCenter.mapper.UserMapper;
import com.UnionPayCenter.mapper.WhiteListMapper;
import com.UnionPayCenter.operation.entity.ActivityConfig;
import com.UnionPayCenter.operation.enums.ActivityOperationTypeEnum;
import com.UnionPayCenter.operation.manager.ActivityConfigManager;
import com.UnionPayCenter.redis.IRedisService;
import com.UnionPayCenter.service.ICouponService;
import com.UnionPayCenter.service.IShopService;
import com.UnionPayCenter.service.IUserService;
import com.UnionPayCenter.service.IWechatService;
import com.UnionPayCenter.service.ServiceTemplate;
import com.UnionPayCenter.sms.StringUtil;
import com.UnionPayCenter.util.CSVUtil;
import com.UnionPayCenter.util.DateFormatUtil;
import com.UnionPayCenter.util.HttpUtil;
import com.UnionPayCenter.util.IdGen;
import com.UnionPayCenter.util.PhoneCheckUtil;
import com.UnionPayCenter.util.StringUtils;
import com.UnionPayCenter.vastchain.SmsUtil;
import com.UnionPayCenter.wechat.WXCardSignatureUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Param:
 * @return:
 * @Author: bite
 * @Date: 2019/4/25
 */
@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS)
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ShopMapper shopMapper;

    @Autowired
    private ChannelMapper channelMapper;

    @Autowired
    private IntegralRuleMapper ruleMapper;

    @Autowired
    private UserIntegralDetailMapper detailMapper;

    @Autowired
    private ManagerMapper managerMapper;

    @Autowired
    private IRedisService redisService;

    @Autowired
    private ConfMapper confMapper;

    @Autowired
    private ShopInfoMapper shopInfoMapper;

    @Autowired
    private UserLevelMapper userLevelMapper;

    @Autowired
    private WhiteListMapper whiteListMapper;

    @Autowired
    private ICouponService couponService;

    @Autowired
    private AccountService accountService;

    @Autowired
    private IShopService shopService;

    @Autowired
    private ChargePayService chargePayService;

    @Autowired
    private ServiceTemplate serviceTemplate;

    @Autowired
    private IWechatService wechatService;

    @Autowired
    protected DistributionOperatorService distributionOperatorService;

    @Autowired
    private DistributionTemplateManager templateManager;

    @Autowired
    private ActivityConfigManager activityConfigManager;

    @Override
    public boolean getVerificationCode(JSONObject jsonParam) {
        String mobile = jsonParam.getString("mobile");
        Integer type = jsonParam.getInteger("type");
        String key = "";
        if (type == null || type == 0) {
            key = "vc:" + mobile;
        } else if (type == 1) {
            key = "vp:" + mobile;
        }
        if (redisService.hasKey("check:" + key)) {
            throw new ApiException(ErrorCodeEnum.SW401);
        }

        String verifyCode = redisService.getString(key);

        if (StringUtils.isNullStr(verifyCode)) {
            verifyCode = String.valueOf((int) Math.round(Math.random() * (9999 - 1000) + 1000));
        }
        SmsUtil.sendVerificationCodeMsg(verifyCode, mobile);
        redisService.setString(key, verifyCode, 5, TimeUnit.MINUTES);
        redisService.setString("check:" + key, "1", 2, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public UserVo registry(JSONObject jsonParam) {
        String code = jsonParam.getString("code");
        String mobile = jsonParam.getString("mobile");
        Integer shopId = jsonParam.getInteger("shopId");

        String vc = redisService.getString("vc:" + mobile);
        if (!code.equals(vc)) {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "验证码"));
        }
        UserVo userEntity = userMapper.selectByMobile(shopId, mobile);
        if (userEntity != null) {
            return userEntity;
        }
        Integer parentId = jsonParam.getInteger("parentId");
        userEntity = new UserVo();
        userEntity.setShopId(shopId);
        userEntity.setMobile(mobile);
        userEntity = this.insertUser(userEntity, parentId, false);
        return userEntity;
    }

    @Override
    public UserEntity getUser(JSONObject jsonParam) {
        Integer userId = jsonParam.getInteger("userId");
        UserEntity userEntity = userMapper.selectByPrimaryKey(userId);
        return userEntity;
    }

    @Override
    public PageInfo<UserVo> findList(JSONObject jsonParam) {
        Integer managerId = jsonParam.getInteger("managerId");
        Integer shopId = jsonParam.getInteger("shopId");
        ManagerEntity managerEntity = managerMapper.selectByShopId(managerId, shopId);
        if (managerEntity != null) {
            //页数
            Integer page = jsonParam.getInteger("page");
            //每页条数
            Integer pageSize = jsonParam.getInteger("pageSize");
            String mobile = jsonParam.getString("mobile");
            String startTime = jsonParam.getString("startTime");
            String endTime = jsonParam.getString("endTime");
            Integer userLevel = jsonParam.getInteger("userLevel");
            Integer startAmount = jsonParam.getInteger("startAmount");
            Integer endAmount = jsonParam.getInteger("endAmount");
            Integer startTotalAmount = jsonParam.getInteger("startTotalAmount");
            Integer endTotalAmount = jsonParam.getInteger("endTotalAmount");

            Page<UserVo> userVos = serviceTemplate.pagination(page, pageSize,
                    () -> userMapper.selectList(shopId, mobile, startTime, endTime, userLevel,
                            startAmount, endAmount, startTotalAmount, endTotalAmount));
            if (CollectionUtils.isEmpty(userVos)) {
                return new PageInfo<>(new ArrayList<>());
            }

            PageInfo<UserVo> pageList = new PageInfo<>(userVos);
            return pageList;
        } else {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "managerId"));
        }
    }

    @Override
    public List<UserVo> findList(UserQuery userQuery, Integer shopId) {
        return userMapper.selectList(shopId, userQuery.getMobile(), userQuery.getStartTime(),
                userQuery.getEndTime(), userQuery.getUserLevel(), userQuery.getStartAmount(),
                userQuery.getEndAmount(), userQuery.getStartTotalAmount(), userQuery.getEndTotalAmount());

    }

    /**
     * 已在SQL查询中实现该功能--2019/07/21
     * <p>
     * 设置用户的账户余额
     * 所有账户余额总和
     *
     * @param userVos     用户信息
     * @param accounts    账户信息
     * @param startAmount 资金筛选起始金额
     * @param endAmount   资金筛选结束金额
     */
    private List<UserVo> setAccountBalance(List<UserVo> userVos, List<Account> accounts, Integer startAmount, Integer endAmount) {
        if (CollectionUtils.isEmpty(accounts)) {
            return userVos;
        }
        Map<Integer, List<Account>> userIdAccountMap = accounts.stream()
                .collect(Collectors.groupingBy(Account::getUserId));
        for (UserVo userVo : userVos) {
            Integer userId = userVo.getUserId();
            List<Account> accountList = userIdAccountMap.get(userId);
            if (CollectionUtils.isEmpty(accountList)) {
                log.debug("user account not created, user id [{}]", userId);
                continue;
            }
            int balance = accountList.stream()
                    .map(Account::getBalance)
                    .reduce(0, (a, b) -> a + b);
            userVo.setBalance(balance);
        }
        return userVos;
    }

    @Override
    public UserEntity findUser(JSONObject jsonParam) {
        Integer managerId = jsonParam.getInteger("managerId");
        Integer shopId = jsonParam.getInteger("shopId");
        ManagerEntity managerEntity = managerMapper.selectByShopId(managerId, shopId);
        if (managerEntity != null) {
            Integer userId = jsonParam.getInteger("userId");
            UserEntity user = userMapper.selectByPrimaryKey(userId);
            if (shopId != user.getShopId()) {
                throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "userId"));
            }
            return user;
        } else {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "managerId"));
        }
    }

    @Override
    public boolean updateUser(JSONObject jsonParam) {
        Integer managerId = jsonParam.getInteger("managerId");
        Integer shopId = jsonParam.getInteger("shopId");
        ManagerEntity managerEntity = managerMapper.selectByShopId(managerId, shopId);
        if (managerEntity != null) {
            Integer userId = jsonParam.getInteger("userId");
            Integer channelId = jsonParam.getInteger("channelId");
            Integer levelId = jsonParam.getInteger("levelId");

            UserEntity user = userMapper.selectByPrimaryKey(userId);
            if (shopId != user.getShopId()) {
                throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "userId"));
            }
            ChannelEntity channel = channelMapper.selectByPrimaryKey(channelId);
            if (channel.getShopId() != user.getShopId()) {
                throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "channelId"));
            }
            user.setChannelId(channelId);
            user.setLevelId(levelId);
            userMapper.updateByPrimaryKeySelective(user);
            return true;
        } else {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "managerId"));
        }
    }

    @Override
    public String exportModel(JSONObject jsonParam) throws IOException {
        Integer managerId = jsonParam.getInteger("managerId");
        Integer shopId = jsonParam.getInteger("shopId");
        ManagerEntity managerEntity = managerMapper.selectByShopId(managerId, shopId);
        if (managerEntity != null) {
            //获取对应商铺
            ShopEntity shopEntity = shopMapper.selectByPrimaryKey(shopId);
            if (shopEntity == null) {
                throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "商铺微信关联信息"));
            }

            List<ChannelEntity> channelList = channelMapper.selectByType(shopId, 2);

            String[] propertyName = new String[5];
            propertyName[0] = "手机号";
            propertyName[1] = "微信号";
            propertyName[2] = "渠道";
            propertyName[3] = "储值余额";
            propertyName[4] = "会员等级";


            List<List<String>> exportData = new ArrayList<List<String>>();
            List<String> row;
            if (channelList != null) {
                for (ChannelEntity channel : channelList) {
                    row = new ArrayList<>();
                    row.add("");
                    row.add("");
                    row.add(channel.getName());
                    row.add("");
                    row.add("");
                    exportData.add(row);
                }
            }

            String name = "/DownloadTemplate-" + DateFormatUtil.getYYYY_MM_DD() + ".xls";
            CSVUtil.poiExcel(name, "会员导入模版", propertyName, exportData);

            ConfEntity confEntity = confMapper.selectByKey(ConstantInterface.DOMAIN_URL);
            return confEntity.getConfValue() + name;
        } else {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "managerId"));
        }
    }

    @Override
    public List<UserVo> importUser(Integer managerId, Integer shopId, MultipartFile file) throws IOException {
        ManagerEntity managerEntity = managerMapper.selectByShopId(managerId, shopId);
        if (managerEntity != null) {
            if (managerEntity.getShopId() == null) {
                throw new ApiException(ErrorCodeEnum.SW200);
            }
            //获取对应商铺
            ShopEntity shopEntity = shopMapper.selectByPrimaryKey(shopId);
            if (shopEntity == null) {
                throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "商铺微信关联信息"));
            }
            List<ChannelEntity> channelList = channelMapper.selectList(shopId, "");
            Map<String, Integer> channelMap = new HashMap<>();
            if (channelList != null) {
                for (ChannelEntity ce : channelList) {
                    channelMap.put(ce.getName(), ce.getChannelId());
                }
            }
            UserLevelEntity userLevelEntity = userLevelMapper.selectDefault(shopId);
            if (userLevelEntity == null) {
                throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "商铺会员等级信息"));
            }

            boolean notNull = false;
            String fileName = file.getOriginalFilename();
            List<UserVo> userList = new ArrayList<>();
            List<UserVo> importList = new ArrayList<>();
            if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$") && !fileName.matches("^.+\\.(?i)(csv)" +
                    "$")) {
                throw new ApiException(ErrorCodeEnum.SW300);
            }
            boolean isExcel2003 = true;
            if (fileName.matches("^.+\\.(?i)(xlsx)$")) {
                isExcel2003 = false;
            }
            InputStream is = file.getInputStream();
            Workbook wb = null;
            if (isExcel2003) {
                wb = new HSSFWorkbook(is);
            } else {
                wb = new XSSFWorkbook(is);
            }
            Sheet sheet = wb.getSheetAt(0);
            if (sheet != null) {
                notNull = true;
            }
            UserVo user;
            DecimalFormat df = new DecimalFormat("#");
            for (int r = 2; r <= sheet.getLastRowNum(); r++) {
                Row row = sheet.getRow(r);//通过sheet表单对象得到 行对象

                String mobile = "";
                if (row.getCell(0).getCellType() == 0) {
                    mobile = df.format(row.getCell(0).getNumericCellValue());
                } else {
                    mobile = row.getCell(0).getStringCellValue();
                }
                if (StringUtils.isNullStr(mobile)) {
                    throw new ApiException(ErrorCodeEnum.SW301.code(), String.format(ErrorCodeEnum.SW301.msg(), (r + 1) + "行，手机号有误"));
                }
                if (!PhoneCheckUtil.isPhoneLegal(mobile)) {
                    throw new ApiException(ErrorCodeEnum.SW301.code(), String.format(ErrorCodeEnum.SW301.msg(), (r + 1) + "行，手机号不规范"));
                }
                String wxAccount = "";
                if (row.getCell(1).getCellType() == 0) {
                    wxAccount = df.format(row.getCell(1).getNumericCellValue());
                } else {
                    wxAccount = row.getCell(1).getStringCellValue();
                }
                if (StringUtils.isNotBlank(wxAccount) && !PhoneCheckUtil.isWechat(wxAccount)) {
                    throw new ApiException(ErrorCodeEnum.SW301.code(), String.format(ErrorCodeEnum.SW301.msg(), (r + 1) + "行，微信号有误"));
                }
                String channel = row.getCell(2).toString();
                if (!channelMap.containsKey(channel)) {
                    throw new ApiException(ErrorCodeEnum.SW301.code(), String.format(ErrorCodeEnum.SW301.msg(), (r + 1) + "行，渠道有误"));
                }
                String balance = row.getCell(3).toString();
                if (StringUtils.isNotBlank(balance) && !StringUtils.isNumeric(balance)) {
                    throw new ApiException(ErrorCodeEnum.SW301.code(), String.format(ErrorCodeEnum.SW301.msg(), (r + 1) + "行，储值余额不是有效自然数"));
                }

                String levelName = row.getCell(4).toString();
                if (StringUtils.isNotBlank(levelName)) {
                    List<UserLevelEntity> levelExists = userLevelMapper.seletByName(shopId, levelName);
                    if (levelExists.size() <= 0) {
                        throw new ApiException(ErrorCodeEnum.SW301.code(), String.format(ErrorCodeEnum.SW301.msg(), (r + 1) + "行，会员等级不存在"));
                    }
                }

                UserVo exists = userMapper.selectByMobile(shopId, mobile);
                if (exists != null) {
                    throw new ApiException(ErrorCodeEnum.SW301.code(), String.format(ErrorCodeEnum.SW301.msg(), (r + 1) + "行，该手机号用户已存在"));
                }

                user = new UserVo();
                user.setMobile(mobile);
                user.setShopId(managerEntity.getShopId());
                user.setWxAccount(wxAccount);
                user.setChannelName(channel);
                if (StringUtils.isNotBlank(balance)) {
                    BigDecimal amount = new BigDecimal(balance);
                    amount = amount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_DOWN);
                    user.setBalance(amount.intValue());
                }
                user.setUserLevelName(levelName);

                importList.add(user);
            }
            //插入数据库
            for (UserVo entity : importList) {
                UserVo usr = this.insertUser(entity, null, true);
                userList.add(usr);
            }

            return userList;
        } else {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "managerId"));
        }
    }

    @Override
    public UserVo getOpenId(JSONObject jsonParam) throws Exception {
        String jsCode = jsonParam.getString("code");
        Integer shopId = jsonParam.getInteger("shopId");

        ShopInfoEntity shopInfoEntity = shopInfoMapper.selectByShop(shopId);
        if (shopInfoEntity == null || StringUtils.isNullStr(shopInfoEntity.getAppId()) || StringUtils.isNullStr(shopInfoEntity.getAppSecret())) {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "shopId"));
        }
        //获取openId
        String urlStr = HttpUtil.WX_OPEN_ID_URL.replace("APPID", shopInfoEntity.getAppId()).replace("APPSECRET",
                shopInfoEntity.getAppSecret()).replace("JSCODE", jsCode);
        String wxResp = HttpUtil.httpGetRequest(urlStr);

        JSONObject respJson = JSONObject.parseObject(wxResp);
        Integer errCode = respJson.getInteger("errcode");
        if (errCode == null || errCode == 0) {
            String openId = respJson.getString("openid");
            String unionId = respJson.getString("unionid");
            String sessionKey = respJson.getString("session_key");
            Integer parentId = jsonParam.getInteger("parentId");

            UserVo user = userMapper.selectByOpenid(shopId, openId);
            if (user == null) {
                //获取分销人id

                //用户不存在 添加
                user = new UserVo();
                user.setShopId(shopId);
                user.setOpenid(openId);
                user.setUnionid(unionId);
                user = this.insertUser(user, parentId, false);
                //已注册用户查询余额，会员等级，等级折扣，优惠券数量
                user = this.getInfo(user);
                DistributionBaseDTO baseDTO = new DistributionBaseDTO();
                baseDTO.setBId(Long.valueOf(shopId));
                baseDTO.setShopId(Long.valueOf(shopId));
                distributionOperatorService.distributionInitUser(baseDTO, parentId == null ? null : Long.valueOf(parentId),
                        Long.valueOf(user.getUserId()));
            } else {
                //已注册用户查询余额，会员等级，等级折扣，优惠券数量
                user = this.getInfo(user);
            }
            setEnableDistribution(user, shopId);
            user.setSessionKey(sessionKey);
            return user;
        } else if (errCode == ErrorCodeEnum.WX1.code()) {
            throw new ApiException(ErrorCodeEnum.WX1.code(), ErrorCodeEnum.WX1.msg());
        } else if (errCode == ErrorCodeEnum.WX40029.code()) {
            throw new ApiException(ErrorCodeEnum.WX40029.code(), ErrorCodeEnum.WX40029.msg());
        } else if (errCode == ErrorCodeEnum.WX45011.code()) {
            throw new ApiException(ErrorCodeEnum.WX45011.code(), ErrorCodeEnum.WX45011.msg());
        }
        return null;
    }

    @Override
    public Map<String, Object> getSignature(JSONObject jsonParam) throws Exception {
        JSONObject wxJson = new JSONObject();
        String cardId = jsonParam.getString("cardId");
        Integer shopId = jsonParam.getInteger("shopId");

        ShopInfoEntity shopInfoEntity = shopInfoMapper.selectByShop(shopId);
        if (shopInfoEntity == null || StringUtils.isNullStr(shopInfoEntity.getWechatId()) || StringUtils.isNullStr(shopInfoEntity.getWechatSecret())) {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "shopId"));
        }

        return WXCardSignatureUtil.getSignature(cardId, null, null, shopInfoEntity.getWechatId(), shopInfoEntity.getWechatSecret());
    }

    @Override
    public UserVo getByMobile(JSONObject jsonParam) {
        Integer shopId = jsonParam.getInteger("shopId");
        String mobile = jsonParam.getString("mobile");
        String openid = jsonParam.getString("openid");
        String unionid = jsonParam.getString("unionid");
        //获取分销人id
        Integer parentId = jsonParam.getInteger("parentId");
        UserVo user = userMapper.selectByOpenid(shopId, openid);
        // 是否需要赠送优惠券
        boolean presentCoupons = false;
        if (user != null) {
            if (StringUtils.isBlank(user.getMobile())) {
                presentCoupons = true;
            }
            user.setMobile(mobile);
            userMapper.updateByPrimaryKeySelective(user);
            //已注册用户查询余额，会员等级，等级折扣，优惠券数量
            user = this.getInfo(user);
            setEnableDistribution(user, shopId);
            if (presentCoupons) {
                presentRegisterCoupons(shopId, mobile);
            }
//            DistributionBaseDTO baseDTO = new DistributionBaseDTO();
//            baseDTO.setBId(Long.valueOf(shopId));
//            baseDTO.setShopId(Long.valueOf(shopId));
//            distributionOperatorService.distributionInitUser(baseDTO, parentId == null ? null : Long.valueOf(parentId),
//                    Long.valueOf(user.getUserId()));
            return user;
        }
        UserVo userEntity = userMapper.selectByMobile(shopId, mobile);
        if (userEntity != null) {
            if (StringUtils.isNullStr(userEntity.getOpenid())) {
                userEntity.setUnionid(unionid);
                userEntity.setOpenid(openid);
                userMapper.updateByPrimaryKeySelective(userEntity);
                //已注册用户查询余额，会员等级，等级折扣，优惠券数量
                userEntity = this.getInfo(userEntity);

                setEnableDistribution(userEntity, shopId);
                return userEntity;
            }
        }
        //用户不存在 添加
        user = new UserVo();
        user.setShopId(shopId);
        user.setMobile(mobile);
        user.setOpenid(openid);
        user.setUnionid(unionid);
        user = this.insertUser(user, parentId, false);
        //已注册用户查询余额，会员等级，等级折扣，优惠券数量
        user = this.getInfo(user);
        return user;
    }

    /**
     * 注册赠送优惠券
     */
    private void presentRegisterCoupons(Integer shopId, String mobile) {
        WhiteListEntity whitelistEntity = whiteListMapper.selectByMobile(mobile, shopId);
        List<WhitelistSetEntity> setList = whiteListMapper.selectByShop(shopId);
        //白名单用户注册赠送优惠券
        if (whitelistEntity != null && setList != null && setList.size() > 0) {
            for (WhitelistSetEntity wlSet : setList) {
                Integer disTypeId = wlSet.getDisTypeId();
                Integer count = wlSet.getCount();
                try {
                    couponService.insertUserCoupon(shopId, mobile, disTypeId, count, CouponConstants.EFFECTIVE_NEXT_DAY_NO);
                } catch (ApiException e) {
                    log.error(String.format("白名单用户注册赠送优惠券失败, 错误信息:%s", e.getMessage()));
                } catch (Exception e) {
                    log.error(String.format("白名单用户注册赠送优惠券失败:"), e);
                }
            }
        }
    }

    /**
     * shopId
     * mobile
     * openid
     * unionid
     * wxAccount   会员导入-微信号
     * channelName 会员导入-渠道名称
     * balance     会员导入-储值余额
     * levelName   会员导入-会员等级
     *
     * @param user        新增或导入的用户信息
     * @param parentId    分销人id
     * @param importOrNot 是否导入用户操作
     * @return 用户信息
     */
    public UserVo insertUser(UserVo user, Integer parentId, boolean importOrNot) {
        //获取线上注册渠道
        Integer shopId = user.getShopId();
        ChannelEntity channelEntity = channelMapper.selectDefault(shopId);
        if (channelEntity == null) {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "商铺微信关联信息"));
        }
        //生成卡号
        String card = DateFormatUtil.dateToNumberString(new Date()) + Math.round(Math.random() * (9999 - 1000) + 1000);

        //获取默认会员等级
        UserLevelEntity userLevelEntity = userLevelMapper.selectDefault(shopId);
        if (userLevelEntity == null) {
            throw new ApiException(ErrorCodeEnum.SW10.code(), String.format(ErrorCodeEnum.SW10.msg(), "商铺会员等级信息"));
        }
        user.setCard(card);
        user.setChannelId(channelEntity.getChannelId());
        user.setLevelId(userLevelEntity.getLevelId());

        String channelName = user.getChannelName();
        //会员导入渠道设置
        if (channelName != null) {
            List<ChannelEntity> channelList = channelMapper.selectList(shopId, channelName);
            Map<String, Integer> channelMap = new HashMap<>();
            if (channelList != null) {
                for (ChannelEntity ce : channelList) {
                    channelMap.put(ce.getName(), ce.getChannelId());
                }
            }
            user.setChannelId(channelMap.get(channelName));
            user.setChannelName(channelName);
        }

        //白名单用户注册赠送会员等级
        String mobile = user.getMobile();
        WhiteListEntity whitelistEntity = whiteListMapper.selectByMobile(mobile, shopId);
        List<WhitelistSetEntity> setList = whiteListMapper.selectByShop(shopId);
        if (whitelistEntity != null && setList != null && setList.size() > 0) {
            if (setList.get(0).getLevelId() != null) {
                user.setLevelId(setList.get(0).getLevelId());
            }
        }

        //取较大的会员等级
        String levelName = user.getUserLevelName();
        if (StringUtils.isNotBlank(levelName)) {
            //获取对应商铺会员等级信息
            List<UserLevelEntity> levelList = userLevelMapper.selectList(shopId);
            Map<String, Integer> levelMap = new HashMap<>();
            if (levelList != null) {
                for (UserLevelEntity le : levelList) {
                    levelMap.put(le.getName(), le.getLevelId());
                }
            }

            Integer levelId = levelMap.get(levelName);
            if (levelId != null && levelId > user.getLevelId()) {
                user.setLevelId(levelId);
            }
        }

        //获取注册赠送积分规则
        // 如果是导入, 不赠送积分
        if (!importOrNot) {
            IntegralRuleEntity ruleEntity = ruleMapper.selectRegistry(shopId);
            if (ruleEntity != null) {
                if (0 == ruleEntity.getIsGive()) {
                    user.setNumber(ruleEntity.getNumber());
                }
            }
            userMapper.insertSelective(user);

            if (ruleEntity != null) {
                if (0 == ruleEntity.getIsGive()) {
                    //注册成功积分明细添加
                    UserIntegralDetailEntity detailEntity = new UserIntegralDetailEntity();
                    detailEntity.setUserId(user.getUserId());
                    detailEntity.setNumber(ruleEntity.getNumber());
                    detailEntity.setRemarks("注册赠送");
                    detailMapper.insertSelective(detailEntity);
                }
            }
        } else {
            userMapper.insertSelective(user);
        }

        //白名单用户注册赠送优惠券
        presentRegisterCoupons(shopId, mobile);

        //账户初始化
        Account account = new Account();
        account.setUserId(user.getUserId());
        account.setShopId(shopId);
        account.setStatus(AccountConstants.STATUS_NORMAL);
        accountService.createAccount(account);

        //会员导入赠送储值余额
        Integer balance = user.getBalance();
        if (Objects.nonNull(balance) && balance > 0) {
            Account chargeAccount = accountService.getAccountByUserId(user.getUserId(), AccountTypeEnum.CHARGE, true);
            chargePayService.userImportCharge(chargeAccount, balance);
        }

        //注册赠送运营场景优惠券
        chargePayService.postRegistRewards(shopId, user.getUserId());

        //创建分销关系初始化
        DistributionBaseDTO baseDTO = new DistributionBaseDTO();
        baseDTO.setBId(Long.valueOf(shopId));
        baseDTO.setShopId(Long.valueOf(shopId));
        distributionOperatorService.distributionInitUser(baseDTO, parentId == null ? null : Long.valueOf(parentId),
                Long.valueOf(user.getUserId()));

        return user;
    }

    public UserVo getInfo(UserVo vo) {
        //查询会员等级 会员折扣
        UserLevelEntity userLevelEntity = userLevelMapper.selectByPrimaryKey(vo.getLevelId());
        if (userLevelEntity != null) {
            vo.setUserLevelName(userLevelEntity.getName());
            vo.setDiscount(userLevelEntity.getDiscount());
        }
        //查询优惠券张数
        int count = couponService.getUserCouponCount(vo.getUserId(), vo.getShopId(), "00");
        vo.setCouponNumber(count);

        queryAccountBalance(vo);

        ShopInfoEntity shopInfo = shopInfoMapper.selectByShop(vo.getShopId());
        vo.setMobileGrant(shopInfo.getMobileGrant());

        setEnableDistribution(vo, vo.getShopId());
        return vo;
    }

    private void queryAccountBalance(UserVo userVo) {
        //查询余额
        Integer userId = userVo.getUserId();
        Account account = accountService.getAccountByUserId(userId, AccountTypeEnum.CHARGE, false);
        if (account != null) {
            userVo.setBalance(account.getBalance());
        } else {
            userVo.setBalance(0);
        }

        Account rewardsAccount = accountService.getAccountByUserId(userId, AccountTypeEnum.CASH_REWARDS, false);
        if (rewardsAccount != null) {
            userVo.setRewardsBalance(rewardsAccount.getBalance());
        } else {
            userVo.setRewardsBalance(0);
        }

        Integer distributionBalance = 0;
        DistributionTemplateDO distributionTemplateDO = getDistributionTemplate(userVo.getShopId());

        if (distributionTemplateDO != null && DistributionConstants.TURE_STRING.equals(distributionTemplateDO.getFlag())) {
            Account distributionAccount = accountService.getAccountByUserId(userId, AccountTypeEnum.DISTRIBUTION_CHARGE, false);
            if (distributionAccount != null) {
                userVo.setDistributionBalance(distributionAccount.getBalance());
            } else {
                userVo.setDistributionBalance(0);
            }
            distributionBalance = userVo.getDistributionBalance();
        } else {
            userVo.setDistributionBalance(null);
        }

        userVo.setTotalBalance(userVo.getRewardsBalance() + userVo.getBalance() + distributionBalance);
    }

    @Override
    public UserVo getUserInfo(Integer userId) {
        UserVo vo = userMapper.selectInfo(userId);
        if (vo != null) {
            vo = this.getInfo(vo);
            return vo;
        }
        return null;
    }

    @Override
    public boolean checkVerificationCode(String mobile, String code, Integer type) {
        String str = "";
        if (type == 0) {
            str = redisService.getString("vc:" + mobile);
        } else if (type == 1) {
            str = redisService.getString("vp:" + mobile);
        }
        return code.equals(str);
    }

    @Override
    public UserEntity wechatPayGetUser(Integer userId) {
        UserEntity user = userMapper.selectByPrimaryKey(userId);
        if (user == null) {
            throw new ApiException(ErrorCodeEnum.WP8005.code(), ErrorCodeEnum.WP8005.msg());
        }
        if (StringUtils.isNullStr(user.getOpenid())) {
            throw new ApiException(ErrorCodeEnum.WP8006.code(), ErrorCodeEnum.WP8006.msg());
        }
        return user;
    }

    @Override
    public UserEntity getUserByMobile(String mobile, Integer shopId) {
        UserEntity userEntity = userMapper.selectByMobile(shopId, mobile);
        return userEntity;
    }

    @Override
    public UserEntity getUserById(Integer userId) {
        UserEntity userEntity = userMapper.selectByPrimaryKey(userId);
        return userEntity;
    }

    @Override
    public List<UserEntity> getUserById(List<Integer> userIds) {
        return userMapper.selectByIds(userIds);
    }

    @Override
    public List<UserEntity> getUserByShopId(Integer shopId) {
        return userMapper.selectByShopId(shopId);
    }

    @Override
    public String exportUserFunds(List<FundsBill> fundsList) throws IOException {
        String[] propertyName = new String[5];
        propertyName[0] = "时间";
        propertyName[1] = "手机号";
        propertyName[2] = "金额";
        propertyName[3] = "类型";
        propertyName[4] = "备注";


        List<List<String>> exportData = new ArrayList<List<String>>();
        List<String> row;
        if (fundsList != null) {
            for (FundsBill fundsVo : fundsList) {
                row = new ArrayList<>();
                row.add(DateFormatUtil.dateToString(fundsVo.getCreateTime()));
                row.add(fundsVo.getMobile());
                row.add(String.valueOf(fundsVo.getAmount() / 100d));
                row.add(fundsVo.getBillType().msg());
                row.add(fundsVo.getRemark());
                exportData.add(row);
            }
        }

        String name = "/UserFundsList-" + DateFormatUtil.getYYYY_MM_DD() + ".xls";
        CSVUtil.poiExcel(name, "会员资金导出", propertyName, exportData);

        ConfEntity confEntity = confMapper.selectByKey(ConstantInterface.DOMAIN_URL);
        return confEntity.getConfValue() + name;
    }


    @Override
    public String exportThirdFunds(List<FundsBillRecordVo> fundsList) throws IOException {
        String[] propertyName = new String[5];
        propertyName[0] = "时间";
        propertyName[1] = "手机号";
        propertyName[2] = "银商订单号";
        propertyName[3] = "金额";
        propertyName[4] = "状态";


        List<List<String>> exportData = new ArrayList<List<String>>();
        List<String> row;
        if (fundsList != null) {
            for (FundsBillRecordVo fundsVo : fundsList) {
                row = new ArrayList<>();
                row.add(fundsVo.getCreateTime());
                row.add(fundsVo.getMobile());
                row.add(fundsVo.getRecordId());
                row.add(String.valueOf(fundsVo.getAmount() / 100d));
                String state = "";
                if(10 == fundsVo.getStatus()){
                    state ="未支付";
                }else if(0 == fundsVo.getStatus()){
                    state ="对外待支付";
                }else if(2 == fundsVo.getStatus()){
                    state ="对外支付中";
                }else if(3 == fundsVo.getStatus()){
                    state ="支付成功";
                }else if(4 == fundsVo.getStatus()){
                    state ="支付失败";
                }
                row.add(state);
                exportData.add(row);
            }
        }

        String name = "/ThirdFundsList-" + DateFormatUtil.getYYYY_MM_DD() + ".xls";
        CSVUtil.poiExcel(name, "第三方资金导出", propertyName, exportData);

        ConfEntity confEntity = confMapper.selectByKey(ConstantInterface.DOMAIN_URL);
        return confEntity.getConfValue() + name;
    }

    @Override
    public UserEntity saveCardNum(JSONObject jsonParam) {
        Integer shopId = jsonParam.getInteger("shopId");
        String mobile = jsonParam.getString("mobile");
        String code = jsonParam.getString("code");
        try {
            ShopEntity shop = shopMapper.selectByPrimaryKey(shopId);
            ShopInfoEntity shopInfoEntity = shopService.wechatGetShopInfo(shopId);
            String cardNum = WXCardSignatureUtil.descryptCode(shopInfoEntity.getWechatId(), shopInfoEntity.getWechatSecret(), code);
            if (StringUtils.isNullStr(cardNum)) {
                throw new ApiException(ErrorCodeEnum.SW12.code(), String.format(ErrorCodeEnum.SW12.msg(), "会员卡号解密"));
            }
            UserVo userEntity = new UserVo();
            userEntity.setShopId(shopId);
            userEntity.setMobile(mobile);
            userEntity.setWxCard(cardNum);
            userMapper.saveCardNumber(userEntity);

            wechatService.sendCardMsg(userEntity, shop, shopInfoEntity);
            return userEntity;
        } catch (ApiException e) {
            throw e;
        } catch (Exception e) {
            throw new ApiException(ErrorCodeEnum.SW12.code(), String.format(ErrorCodeEnum.SW12.msg(), "会员卡号保存"));
        }
    }

    @Override
    public UserVo getByWxCard(String wxCard) {
        UserVo user = userMapper.findByWxCard(wxCard);
        if (user != null) {
            //已注册用户查询余额，会员等级，等级折扣，优惠券数量
            user = this.getInfo(user);
        }
        return user;
    }

    @Override
    public boolean saveOpenid(JSONObject jsonParam) {
        Integer shopId = jsonParam.getInteger("shopId");
        String openid = jsonParam.getString("openid");
        String unionid = jsonParam.getString("unionid");
        UserEntity userEntity = new UserEntity();
        userEntity.setOpenid(openid);
        userEntity.setShopId(shopId);
        userEntity.setUnionid(unionid);
        userMapper.updateUnionid(userEntity);
        return true;
    }

    @Override
    public boolean getPaidUnionId(Integer userId, String paymentProviderTrxId) throws Exception {
        UserEntity user = userMapper.selectByPrimaryKey(userId);
        //如果unionid为空则根据支付后获取
        if (StringUtils.isNullStr(user.getUnionid())) {
            ShopInfoEntity shopInfo = shopInfoMapper.selectByShop(user.getShopId());
            String unionId = WXCardSignatureUtil.getPaidUnionId(shopInfo.getAppId(), shopInfo.getAppSecret(), paymentProviderTrxId,
                    user.getOpenid());

            user.setUnionid(unionId);
            userMapper.updateByPrimaryKeySelective(user);
        }
        return true;
    }

    @Override
    public UserVo getByUnionid(String unionId, Integer shopId) {
        UserVo user = userMapper.getByUnionid(unionId, shopId);
        if (user != null) {
            //已注册用户查询余额，会员等级，等级折扣，优惠券数量
            user = this.getInfo(user);
        }
        return user;
    }

    @Override
    public boolean saveFormid(JSONObject jsonParam) {
        Integer shopId = jsonParam.getInteger("shopId");
        String openid = jsonParam.getString("openid");
        UserEntity user = userMapper.selectByOpenid(shopId, openid);
        if (user != null) {
            String formid = jsonParam.getString("formid");

            Set<String> keys = redisService.keys("formId:" + user.getUserId() + ":");
            if (keys == null || keys.size() < 10) {
                redisService.setString("formId:" + user.getUserId() + ":" + IdGen.newStringId(), formid, 7, TimeUnit.DAYS);
            }
            return true;
        }
        return false;
    }

    @Override
    public String getUnlimited(JSONObject jsonParam) {
        Integer shopId = jsonParam.getInteger("shopId");
        Integer userId = jsonParam.getInteger("userId");
        String name = "/unlimited-" + userId + ".jpg";

//        String page = "pages/getCoupon/index";
        String page = "pages/user/index";
        String path = ClassUtils.getDefaultClassLoader().getResource("").getPath() + "static" + name;
        boolean isSucc = wechatService.getUnlimited(shopId, userId, path, page);
        if (isSucc) {
            ConfEntity confEntity = confMapper.selectByKey(ConstantInterface.DOMAIN_URL);
            return confEntity.getConfValue() + name;
        }
        throw new ApiException(ErrorCodeEnum.WD11001);
    }

    @Override
    public boolean updateBaseInfo(String plainText) {
        JSONObject json = JSONObject.parseObject(plainText);
        if (json.containsKey("nickName")) {
            String openId = json.getString("openId");
            String unionId = json.getString("unionId");
            String nickName = json.getString("nickName");
            String country = json.getString("country");
            String province = json.getString("province");
            String city = json.getString("city");
            String avatarUrl = json.getString("avatarUrl");
            String gender = json.getString("gender");


            UserEntity user = new UserEntity();
            user.setOpenid(openId);
            user.setUnionid(unionId);
            user.setSex(SexEnum.getEnum(StringUtils.isNullStr(gender) ? 2 : Integer.valueOf(gender)));
            user.setNick(nickName);
            user.setHead(avatarUrl);
            user.setArea(country + "/" + province + "/" + city);
            userMapper.updateByOpenid(user);

            return true;
        }
        return false;
    }

    private DistributionTemplateDO getDistributionTemplate(Integer shopId) {
        DistributionShareDTO distributionShareDTO = new DistributionShareDTO();
        distributionShareDTO.setBId(Long.valueOf(shopId));
        distributionShareDTO.setShopId(Long.valueOf(shopId));
        return templateManager.queryTemplate(distributionShareDTO);
    }

    protected void setEnableDistribution(UserVo user, Integer shopId) {
        DistributionTemplateDO distributionTemplateDO = getDistributionTemplate(shopId);
        if (distributionTemplateDO != null && StringUtils.isNotBlank(distributionTemplateDO.getFlag())) {
            user.setEnableDistribution(Integer.valueOf(distributionTemplateDO.getFlag()));
        } else {
            user.setEnableDistribution(0);
        }
        ActivityConfig activityConfig = activityConfigManager.getByShopId(shopId, ActivityOperationTypeEnum.BALANCE_PAY);
        if (activityConfig == null) {
            user.setEnableBalancePayCoupon(1);
            user.setEnableBalancePayMemberCardDiscount(1);
        } else {
            user.setEnableBalancePayCoupon(activityConfig.getCoupon());
            user.setEnableBalancePayMemberCardDiscount(activityConfig.getMemberCardDiscount());
        }
    }

    /**
     * @param userId  用户ID
     * @param levelId 会员等级
     */
    @Override
    public void updateUserLevel(Integer userId, Integer levelId) {
        UserEntity exists = userMapper.selectInfo(userId);
        if (Objects.isNull(exists)) {
            return;
        }
        UserEntity entity = new UserEntity();
        UserLevelEntity newUserLevel = userLevelMapper.selectByPrimaryKey(levelId);
        UserLevelEntity existsUserLevel = userLevelMapper.selectByPrimaryKey(exists.getLevelId());
        if (null == existsUserLevel) {
            entity.setUserId(userId);
            entity.setLevelId(levelId);
            userMapper.updateByPrimaryKeySelective(entity);
        } else {
            if (Objects.nonNull(newUserLevel) && Objects.nonNull(newUserLevel.getSort()) && newUserLevel.getSort() > existsUserLevel.getSort()) {
                entity.setUserId(userId);
                entity.setLevelId(levelId);
                userMapper.updateByPrimaryKeySelective(entity);
            }
        }

    }
}
